{
 "cells": [
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-26T05:43:19.913298Z",
     "start_time": "2025-01-26T05:43:16.522599Z"
    }
   },
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "    \n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=12, micro=3, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 1.26.4\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.1\n",
      "torch 2.5.1+cpu\n",
      "cpu\n"
     ]
    }
   ],
   "execution_count": 1
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 准备数据\n",
    "### 1.加载数据集"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-26T05:43:28.768027Z",
     "start_time": "2025-01-26T05:43:28.681499Z"
    }
   },
   "source": [
    "from sklearn.datasets import fetch_california_housing\n",
    "\n",
    "housing = fetch_california_housing(data_home='data')\n",
    "#print(housing.DESCR)\n",
    "print(housing.data.shape)\n",
    "print(housing.target.shape)"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(20640, 8)\n",
      "(20640,)\n"
     ]
    }
   ],
   "execution_count": 2
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-26T05:44:17.090739Z",
     "start_time": "2025-01-26T05:44:17.086493Z"
    }
   },
   "source": [
    "import pprint  #打印的格式比较好看\n",
    "\n",
    "pprint.pprint(housing.data[0:2])\n",
    "print('-'*50)\n",
    "pprint.pprint(housing.target[0:2])"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "array([[ 8.32520000e+00,  4.10000000e+01,  6.98412698e+00,\n",
      "         1.02380952e+00,  3.22000000e+02,  2.55555556e+00,\n",
      "         3.78800000e+01, -1.22230000e+02],\n",
      "       [ 8.30140000e+00,  2.10000000e+01,  6.23813708e+00,\n",
      "         9.71880492e-01,  2.40100000e+03,  2.10984183e+00,\n",
      "         3.78600000e+01, -1.22220000e+02]])\n",
      "--------------------------------------------------\n",
      "array([4.526, 3.585])\n"
     ]
    }
   ],
   "execution_count": 3
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "### 2.预处理数据：拆分训练集、验证集、测试集"
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-26T05:46:33.874017Z",
     "start_time": "2025-01-26T05:46:33.829036Z"
    }
   },
   "source": [
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "# 拆分训练集和测试集，random_state是随机种子,同样的随机数种子，是为了得到同样的随机值\n",
    "x_train_all, x_test, y_train_all, y_test = train_test_split(   #默认是0.25\n",
    "    housing.data, housing.target, random_state = 7)\n",
    "# 拆分训练集和验证集\n",
    "x_train, x_valid, y_train, y_valid = train_test_split(\n",
    "    x_train_all, y_train_all, random_state = 11)\n",
    "\n",
    "# 训练集\n",
    "print(x_train.shape, y_train.shape)\n",
    "# 验证集\n",
    "print(x_valid.shape, y_valid.shape)\n",
    "# 测试集\n",
    "print(x_test.shape, y_test.shape)\n",
    "\n",
    "# 把3个数据集都放到字典里，每个是列表，里面是ndarray类型的数据\n",
    "dataset_maps = {\n",
    "    \"train\": [x_train, y_train],\n",
    "    \"valid\": [x_valid, y_valid],\n",
    "    \"test\": [x_test, y_test],\n",
    "}"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(11610, 8) (11610,)\n",
      "(3870, 8) (3870,)\n",
      "(5160, 8) (5160,)\n"
     ]
    }
   ],
   "execution_count": 4
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-26T05:48:51.990124Z",
     "start_time": "2025-01-26T05:48:51.981074Z"
    }
   },
   "source": [
    "from sklearn.preprocessing import StandardScaler\n",
    "\n",
    "scaler = StandardScaler() # 标准化\n",
    "# 这里只fit，不transform，因为后面要用transform\n",
    "scaler.fit(x_train) # fit和fit_transform的区别，fit是计算均值和方差，fit_transform是先fit，然后transform"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "StandardScaler()"
      ],
      "text/html": [
       "<style>#sk-container-id-1 {\n",
       "  /* Definition of color scheme common for light and dark mode */\n",
       "  --sklearn-color-text: #000;\n",
       "  --sklearn-color-text-muted: #666;\n",
       "  --sklearn-color-line: gray;\n",
       "  /* Definition of color scheme for unfitted estimators */\n",
       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
       "  --sklearn-color-unfitted-level-3: chocolate;\n",
       "  /* Definition of color scheme for fitted estimators */\n",
       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
       "\n",
       "  /* Specific color for light theme */\n",
       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-icon: #696969;\n",
       "\n",
       "  @media (prefers-color-scheme: dark) {\n",
       "    /* Redefinition of color scheme for dark theme */\n",
       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-icon: #878787;\n",
       "  }\n",
       "}\n",
       "\n",
       "#sk-container-id-1 {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 pre {\n",
       "  padding: 0;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-hidden--visually {\n",
       "  border: 0;\n",
       "  clip: rect(1px 1px 1px 1px);\n",
       "  clip: rect(1px, 1px, 1px, 1px);\n",
       "  height: 1px;\n",
       "  margin: -1px;\n",
       "  overflow: hidden;\n",
       "  padding: 0;\n",
       "  position: absolute;\n",
       "  width: 1px;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-dashed-wrapped {\n",
       "  border: 1px dashed var(--sklearn-color-line);\n",
       "  margin: 0 0.4em 0.5em 0.4em;\n",
       "  box-sizing: border-box;\n",
       "  padding-bottom: 0.4em;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-container {\n",
       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
       "     so we also need the `!important` here to be able to override the\n",
       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
       "  display: inline-block !important;\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-text-repr-fallback {\n",
       "  display: none;\n",
       "}\n",
       "\n",
       "div.sk-parallel-item,\n",
       "div.sk-serial,\n",
       "div.sk-item {\n",
       "  /* draw centered vertical line to link estimators */\n",
       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
       "  background-size: 2px 100%;\n",
       "  background-repeat: no-repeat;\n",
       "  background-position: center center;\n",
       "}\n",
       "\n",
       "/* Parallel-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item::after {\n",
       "  content: \"\";\n",
       "  width: 100%;\n",
       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
       "  flex-grow: 1;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel {\n",
       "  display: flex;\n",
       "  align-items: stretch;\n",
       "  justify-content: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:first-child::after {\n",
       "  align-self: flex-end;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:last-child::after {\n",
       "  align-self: flex-start;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:only-child::after {\n",
       "  width: 0;\n",
       "}\n",
       "\n",
       "/* Serial-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-serial {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "  align-items: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  padding-right: 1em;\n",
       "  padding-left: 1em;\n",
       "}\n",
       "\n",
       "\n",
       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
       "clickable and can be expanded/collapsed.\n",
       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
       "*/\n",
       "\n",
       "/* Pipeline and ColumnTransformer style (default) */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable {\n",
       "  /* Default theme specific background. It is overwritten whether we have a\n",
       "  specific estimator or a Pipeline/ColumnTransformer */\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "/* Toggleable label */\n",
       "#sk-container-id-1 label.sk-toggleable__label {\n",
       "  cursor: pointer;\n",
       "  display: flex;\n",
       "  width: 100%;\n",
       "  margin-bottom: 0;\n",
       "  padding: 0.5em;\n",
       "  box-sizing: border-box;\n",
       "  text-align: center;\n",
       "  align-items: start;\n",
       "  justify-content: space-between;\n",
       "  gap: 0.5em;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label .caption {\n",
       "  font-size: 0.6rem;\n",
       "  font-weight: lighter;\n",
       "  color: var(--sklearn-color-text-muted);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:before {\n",
       "  /* Arrow on the left of the label */\n",
       "  content: \"▸\";\n",
       "  float: left;\n",
       "  margin-right: 0.25em;\n",
       "  color: var(--sklearn-color-icon);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "/* Toggleable content - dropdown */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content {\n",
       "  max-height: 0;\n",
       "  max-width: 0;\n",
       "  overflow: hidden;\n",
       "  text-align: left;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content pre {\n",
       "  margin: 0.2em;\n",
       "  border-radius: 0.25em;\n",
       "  color: var(--sklearn-color-text);\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted pre {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
       "  /* Expand drop-down */\n",
       "  max-height: 200px;\n",
       "  max-width: 100%;\n",
       "  overflow: auto;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
       "  content: \"▾\";\n",
       "}\n",
       "\n",
       "/* Pipeline/ColumnTransformer-specific style */\n",
       "\n",
       "#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator-specific style */\n",
       "\n",
       "/* Colorize estimator box */\n",
       "#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label label.sk-toggleable__label,\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  /* The background is the default theme color */\n",
       "  color: var(--sklearn-color-text-on-default-background);\n",
       "}\n",
       "\n",
       "/* On hover, darken the color of the background */\n",
       "#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "/* Label box, darken color on hover, fitted */\n",
       "#sk-container-id-1 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator label */\n",
       "\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  font-family: monospace;\n",
       "  font-weight: bold;\n",
       "  display: inline-block;\n",
       "  line-height: 1.2em;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label-container {\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "/* Estimator-specific */\n",
       "#sk-container-id-1 div.sk-estimator {\n",
       "  font-family: monospace;\n",
       "  border: 1px dotted var(--sklearn-color-border-box);\n",
       "  border-radius: 0.25em;\n",
       "  box-sizing: border-box;\n",
       "  margin-bottom: 0.5em;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "/* on hover */\n",
       "#sk-container-id-1 div.sk-estimator:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
       "\n",
       "/* Common style for \"i\" and \"?\" */\n",
       "\n",
       ".sk-estimator-doc-link,\n",
       "a:link.sk-estimator-doc-link,\n",
       "a:visited.sk-estimator-doc-link {\n",
       "  float: right;\n",
       "  font-size: smaller;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1em;\n",
       "  height: 1em;\n",
       "  width: 1em;\n",
       "  text-decoration: none !important;\n",
       "  margin-left: 0.5em;\n",
       "  text-align: center;\n",
       "  /* unfitted */\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted,\n",
       "a:link.sk-estimator-doc-link.fitted,\n",
       "a:visited.sk-estimator-doc-link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "/* Span, style for the box shown on hovering the info icon */\n",
       ".sk-estimator-doc-link span {\n",
       "  display: none;\n",
       "  z-index: 9999;\n",
       "  position: relative;\n",
       "  font-weight: normal;\n",
       "  right: .2ex;\n",
       "  padding: .5ex;\n",
       "  margin: .5ex;\n",
       "  width: min-content;\n",
       "  min-width: 20ex;\n",
       "  max-width: 50ex;\n",
       "  color: var(--sklearn-color-text);\n",
       "  box-shadow: 2pt 2pt 4pt #999;\n",
       "  /* unfitted */\n",
       "  background: var(--sklearn-color-unfitted-level-0);\n",
       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted span {\n",
       "  /* fitted */\n",
       "  background: var(--sklearn-color-fitted-level-0);\n",
       "  border: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link:hover span {\n",
       "  display: block;\n",
       "}\n",
       "\n",
       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link {\n",
       "  float: right;\n",
       "  font-size: 1rem;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1rem;\n",
       "  height: 1rem;\n",
       "  width: 1rem;\n",
       "  text-decoration: none;\n",
       "  /* unfitted */\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "#sk-container-id-1 a.estimator_doc_link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "</style><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>StandardScaler()</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" checked><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow\"><div><div>StandardScaler</div></div><div><a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.6/modules/generated/sklearn.preprocessing.StandardScaler.html\">?<span>Documentation for StandardScaler</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></div></label><div class=\"sk-toggleable__content fitted\"><pre>StandardScaler()</pre></div> </div></div></div></div>"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 6
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": "### 3.构建数据集"
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-26T05:48:52.964158Z",
     "start_time": "2025-01-26T05:48:52.955783Z"
    }
   },
   "source": [
    "#构建私有数据集，就需要用如下方式\n",
    "from torch.utils.data import Dataset\n",
    "\n",
    "class HousingDataset(Dataset):\n",
    "    def __init__(self, mode='train'):\n",
    "        self.x, self.y = dataset_maps[mode] # x,y都是ndarray类型，这里dataset_maps是全局变量，也可以传参传进来\n",
    "        self.x = torch.from_numpy(scaler.transform(self.x)).float() # from_numpy将NumPy数组转换成PyTorch张量\n",
    "        self.y = torch.from_numpy(self.y).float().reshape(-1, 1) # 处理为多行1列的tensor类型，因为__getitem__切片时需要\n",
    "            \n",
    "    def __len__(self): #必须写\n",
    "        return len(self.x) #返回数据集的长度,0维的size\n",
    "    \n",
    "    # idx是索引，返回的是数据和标签，这里是一个样本\n",
    "    #重写后： for i in xxx:这样i能取出迭代器xxx里的东西\n",
    "    def __getitem__(self, idx): \n",
    "        return self.x[idx], self.y[idx]\n",
    "    \n",
    "#train_ds是dataset类型的数据，与前面例子的FashionMNIST类型一致\n",
    "train_ds = HousingDataset(\"train\")\n",
    "valid_ds = HousingDataset(\"valid\")\n",
    "test_ds = HousingDataset(\"test\")"
   ],
   "outputs": [],
   "execution_count": 7
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": "### 4.DataLoader"
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-26T05:49:22.378380Z",
     "start_time": "2025-01-26T05:49:22.374376Z"
    }
   },
   "source": [
    "from torch.utils.data import DataLoader\n",
    "\n",
    "#放到DataLoader中的train_ds, valid_ds, test_ds都是dataset类型的数据\n",
    "batch_size = 32  # 可调的超参数，一般取16、32、64、128等\n",
    "train_loader = DataLoader(train_ds, batch_size=batch_size, shuffle=True)\n",
    "val_loader = DataLoader(valid_ds, batch_size=batch_size, shuffle=False)\n",
    "test_loader = DataLoader(test_ds, batch_size=batch_size, shuffle=False)"
   ],
   "outputs": [],
   "execution_count": 8
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": "# 定义模型"
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-26T05:50:11.402373Z",
     "start_time": "2025-01-26T05:50:11.398715Z"
    }
   },
   "source": [
    "class NeuralNetwork(nn.Module):\n",
    "    def __init__(self, input_dim=8):\n",
    "        super().__init__()\n",
    "        self.linear_relu_stack = nn.Sequential(  # 参数8*30+30+30*1+1=301\n",
    "            nn.Linear(input_dim, 30),\n",
    "            nn.ReLU(),\n",
    "            nn.Linear(30, 1)\n",
    "            )\n",
    "        \n",
    "    def forward(self, x):\n",
    "        # x.shape [batch size, 8]\n",
    "        logits = self.linear_relu_stack(x)\n",
    "        # logits.shape [batch size, 1]\n",
    "        return logits"
   ],
   "outputs": [],
   "execution_count": 9
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-26T05:52:05.098948Z",
     "start_time": "2025-01-26T05:52:05.094995Z"
    }
   },
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = -1\n",
    "        self.counter = 0\n",
    "        \n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter \n",
    "            self.counter = 0\n",
    "        else: \n",
    "            self.counter += 1\n",
    "            \n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ],
   "outputs": [],
   "execution_count": 10
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-26T05:52:06.013404Z",
     "start_time": "2025-01-26T05:52:06.009550Z"
    }
   },
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    for datas, labels in dataloader:\n",
    "        datas = datas.to(device)\n",
    "        labels = labels.to(device)\n",
    "        # 前向计算：只有损失没有准确率\n",
    "        logits = model(datas)\n",
    "        loss = loss_fct(logits, labels)         # 验证集损失\n",
    "        loss_list.append(loss.item())\n",
    "        \n",
    "    return np.mean(loss_list)\n"
   ],
   "outputs": [],
   "execution_count": 11
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-26T05:53:13.372155Z",
     "start_time": "2025-01-26T05:52:07.451046Z"
    }
   },
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "    \n",
    "    global_step = 0\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):  # 100*726=72600\n",
    "            # training\n",
    "            for datas, labels in train_loader:# 11610/16=726\n",
    "                datas = datas.to(device)\n",
    "                labels = labels.to(device)\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "                # 模型前向计算\n",
    "                logits = model(datas) #得到预测值\n",
    "                # 计算损失\n",
    "                loss = loss_fct(logits, labels)\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    " \n",
    "                loss = loss.cpu().item() #转为cpu类型，item()是取值\n",
    "                # record\n",
    "                \n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"step\": global_step\n",
    "                })\n",
    "                \n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "\n",
    "                    # 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                         # 根据验证集的损失来实现早停\n",
    "                         # 加-是因为loss越小越好，同时early_stop用的是metric越来越好，为了复用\n",
    "                        early_stop_callback(-val_loss)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "                    \n",
    "                # update step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "                pbar.set_postfix({\"epoch\": epoch_id})\n",
    "        \n",
    "    return record_dict\n",
    "        \n",
    "\n",
    "epoch = 100\n",
    "model = NeuralNetwork()\n",
    "\n",
    "# 1. 定义损失函数 采用MSE损失,均方误差\n",
    "loss_fct = nn.MSELoss()\n",
    "# 2. 定义优化器 采用SGD\n",
    "optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)\n",
    "\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=10, min_delta=1e-3)\n",
    "\n",
    "model = model.to(device)\n",
    "record = training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    early_stop_callback=early_stop_callback,\n",
    "    eval_step=len(train_loader)\n",
    "    )"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  0%|          | 0/72600 [00:00<?, ?it/s]"
      ],
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "e647e96a6af44e79bcb888eab320d6ce"
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n",
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n",
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n",
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n",
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n",
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n",
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n",
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n",
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n",
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n",
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n",
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n",
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n",
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n",
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n",
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n",
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n",
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n",
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n",
      "IOPub message rate exceeded.\n",
      "The Jupyter server will temporarily stop sending output\n",
      "to the client in order to avoid crashing it.\n",
      "To change this limit, set the config variable\n",
      "`--ServerApp.iopub_msg_rate_limit`.\n",
      "\n",
      "Current values:\n",
      "ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
      "ServerApp.rate_limit_window=3.0 (secs)\n",
      "\n"
     ]
    }
   ],
   "execution_count": 12
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-26T05:53:56.933426Z",
     "start_time": "2025-01-26T05:53:56.822231Z"
    }
   },
   "source": [
    "#画线要注意的是损失是不一定在零到1之间的\n",
    "def plot_learning_curves(record_dict, sample_step=500):\n",
    "    # build DataFrame\n",
    "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
    "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
    "\n",
    "    # plot\n",
    "    for idx, item in enumerate(train_df.columns):\n",
    "        plt.plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
    "        plt.plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
    "        plt.grid()\n",
    "        plt.legend()\n",
    "        # plt.xticks(range(0, train_df.index[-1], 10*sample_step), range(0, train_df.index[-1], 10*sample_step))\n",
    "        plt.xlabel(\"step\")\n",
    "\n",
    "        plt.show()\n",
    "\n",
    "plot_learning_curves(record)  #横坐标是 steps"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ],
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAhYAAAGwCAYAAAD16iy9AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAdbBJREFUeJztnQd4VFX6xr9JJyEBQuiE3nsVsSBKUXTtq66wKva66rpr26Kga1/969rWXRVdFcUGNqpUkd577z2U9J7M/3nPvWdyZzKTApOZyZ339zyXSSaXmXtuOec9XzsOp9PpFEIIIYQQPxDhjw8hhBBCCAEUFoQQQgjxGxQWhBBCCPEbFBaEEEII8RsUFoQQQgjxGxQWhBBCCPEbFBaEEEII8RtREmBKS0vl0KFDkpiYKA6HI9BfTwghhJDTAGWvsrKypHnz5hIRERE6wgKiIjU1NdBfSwghhBA/sH//fmnZsmXoCAtYKvSBJSUl+e1zi4qKZObMmTJy5EiJjo6WcIPtZ/vZfraf7Wf7o2uw/ZmZmcowoMfxkBEW2v0BUeFvYREfH68+M1xvLLaf7Wf72X62n+2vaSoLY2DwJiGEEEL8BoUFIYQQQvwGhQUhhBBC/EbAYywIIYTYD5QSKCwsDFqMQVRUlOTn50tJSYmEG0V+aj/iMyIjI8/4eCgsCCGEnBEQFLt371biIlj1FZo2baqyDcOxPpLTj+2vX7+++qwz+RwKC0IIIWc0qB0+fFjNdJGKWFHhpJoCgiY7O1vq1q0blO8PNqV+aD+uY25urhw7dkz93qxZs9M+HgoLQgghp01xcbEakFCNESmPwXTDxMXFha2wKPRD++vUqaNeIS4aN2582m6R8LsChBBC/Ib26cfExAT7UIgf0OIQcRunC4UFIYSQMyYcYxvsiMMP15HCghBCCCF+g8KCEEIIIX6DwoIQQgg5A9q0aSOvv/66Xz5r3rx5yh2Rnp4utRX7ZIVkHZb4gjSRkkJU+Qj20RBCCAlhhg4dKn369PGLIFi+fLkkJCT45bjsgG2ERdQHF8mInDQpOnegSIvewT4cQgghtRjUdUDGCypaVkajRo0Ccky1Bfu4QiLMi19aHOwjIYSQsEUVWiosDviWV1iivrsqjB07VubPny9vvPGGcjtg++ijj9TrtGnTpH///hIbGysLFy6UnTt3ypVXXilNmjRRBagGDhwoP//8c4WuEIfDIe+//75cffXVKn2zY8eO8v3335/2Of3mm2+ke/fu6pjwXa+++qrb39999111zPguHOdvf/tb19++/vpr6dmzp6pR0bBhQxk+fLjk5ORITRJlN2HhKKGwIISQYJFXVCLdnpoRlO/eMG6E1K1CUScIim3btkmPHj3kmWeeUe9t3LhRvT7xxBPyz3/+U9q1aycNGjRQZbIvvfRSee6559TA/r///U8uv/xy2bp1q7Rq1crnd4wfP15efvlleeWVV+TNN9+UMWPGyN69eyU5OblabVq5cqVcf/31Mm7cOLnhhhtk0aJFct999ymRAIG0YsUKeeihh+Tf//63DBs2TMVm/PLLL+r/oiLqjTfeqI4DIicrK0v9raoC7HSxnbCQ0tMv6kEIIcT+1KtXTxX0wgwf62KALVu2qFcIjREjRrj2hRDo3bvMvf7ss8/K5MmTlQXigQce8PkdY8eOVYM6eP755+Vf//qXLFu2TC655JJqHetrr72mBMPf//539XunTp1k06ZNSrDgO/bt26fiOy6++GJp0aKFtG3bVvr27esSFqiMes0110jr1q3Ve7Be1DT2ERaRZsAmXSGEEBI06kRHyqZnLg54SeuszCz13WfKgAED3H7HGhywFvz000+ugTovL08N6BXRq1cv188Y+JOSklzrcFSHzZs3K1eMlXPPPVe5XhADAhEE0QAxAdEyatQolwsGggiiBGICwmPkyJHKTQJLTMjEWODkan+U3rp06SIhAWMsCCEk6GBciI+JCvhWJybSL1UjPbM7/vznPysLBawOcCOsWbNGDdSVLREf7ZGdiGOridVfExMTlTsEMR1YOOypp55SggIuEaz1MWvWLBU30q1bN+WS6dy5s1qJNqSCNxFAAtWmNwS3hJSwKKErhBBCSMXAFaLXOamIX3/9VbkcYAWAoIDrZM+ePRIounbtqo7B85jgEtGLhCFzBemzL730kqxbt04d35w5c1yCBhYOxHysXr1atRtCKaRcIWiA9kmFEs6IaFFalRYLQgghlYDsiqVLl6pBGNkevqwJyOj49ttvVcAmBmnEOtSE5cEXf/rTn1QmCmI7ELy5ePFieeutt+Sdd95Rf//xxx9V5kq/fv2kZcuWMn36dHV8sEygfbNnz1YuEKxWit/T0tKUWAkpYbF9+3a1PC6WZx08eLC88MILFUbG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JPhQWBBCCCHEb1BYEEIIIcRvUFgQQgghxG/YRli8/fbb0qZNG4mLi5NBgwbJsmXLJNRZsGCBXH755aqKGaqQTpkyxe3viKt96qmnpFmzZlKnTh0ZPny4bN++3W2fkydPypgxY1RRlPr168vtt98u2dnZbvusW7dOzj//fHVuUJ3t5ZdfLncsX331lXTp0kXt07NnT5k6darUJC+88IIMHDhQVWBt3LixXHXVVbJ161a3ffLz8+X++++Xhg0bSt26deXaa6+Vo0ePuu2zb98+ueyyyyQ+Pl59zqOPPirFxcVu+8ybN0/69eunIqY7dOggH330UdDvn3fffVd69erlKmgzePBgmTZtWli03RsvvviiegYefvjhsDgH48aNU+21bnj+wqHtmoMHD8rvf/971Ub0b+h3VqxYERb9H8A597wHsOG61/p7wGkDvvjiC2dMTIzzww8/dG7cuNF55513OuvXr+88evSoM5SZOnWq869//avz22+/RWaOc/LkyW5/f/HFF5316tVzTpkyxbl27VrnFVdc4Wzbtq0zLy/Ptc8ll1zi7N27t3PJkiXOX375xdmhQwfnjTfe6Pp7RkaGs0mTJs4xY8Y4N2zY4Pz888+dderUcb733nuufX799VdnZGSk8+WXX3Zu2rTJ+be//c0ZHR3tXL9+fY21/eKLL3ZOmDBBHdOaNWucl156qbNVq1bO7Oxs1z733HOPMzU11Tl79mznihUrnGeffbbznHPOcf29uLjY2aNHD+fw4cOdq1evVuczJSXF+eSTT7r22bVrlzM+Pt75yCOPqLa9+eabqq3Tp08P6v3z/fffO3/66Sfntm3bnFu3bnX+5S9/Uecc58Pubfdk2bJlzjZt2jh79erlfOihh1zv2/kcPP30087u3bs7Dx8+7NrS0tLCou3g5MmTztatWzvHjh3rXLp0qTrWGTNmOHfs2BEW/R84duyY2/WfNWuWGgfmzp1b6+8BWwiLs846y3n//fe7fi8pKXE2b97c+cILLzhrC57CorS01Nm0aVPnK6+84novPT3dGRsbqx4OgBsF/2/58uWufaZNm+Z0OBzOgwcPqt/feecdZ4MGDZwFBQWufR5//HFn586dXb9ff/31zssuu8zteAYNGuS8++67nYECDxnaMn/+fFdb8XB/9dVXrn02b96s9lm8eLH6HQ9SRESE88iRI659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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 13
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 测试集"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-26T05:54:11.092967Z",
     "start_time": "2025-01-26T05:54:11.034075Z"
    }
   },
   "source": [
    "model.eval()\n",
    "loss = evaluating(model, test_loader, loss_fct)\n",
    "print(f\"loss:     {loss:.4f}\")"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss:     0.3160\n"
     ]
    }
   ],
   "execution_count": 14
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "pytorch",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.8"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
